﻿/* 
 *  <copyright file="ModelCmp.cs" company="Wesley Jowitt">
 *  
 *  SobrietyEngine: Copyright (c) 2011 Wesley Jowitt
 *  
 *  This software is distributed under a software license.
 *   * As of the creation time of this file, the license in use is the Microsoft Permissive License (Ms-PL) 
 *    * The license can be viewed at:
 *     * License.Txt in the root folder of this project
 *      * http://sobrietyengine.codeplex.com/license
 *       * http://www.microsoft.com/opensource/licenses.mspx
 *       
 *  </copyright>
 *   */

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SobrietyEngine.Actor;
using SobrietyEngine.Common;
using SobrietyEngine.Components;
using SobrietyEngine.Components.Common;
using SobrietyEngine.Components.Physics;
using SobrietyEngine.Geometry;
using SobrietyEngine.Logging;
using SobrietyEngine.Scene;

namespace SEGameExample.Components.Spawners
{
    public class StaticGeomFromMeshCmp : BaseComponent, SobrietyEngine.Components.IComponent
    {
        String modelName;
        float shortEdgeTol = 0.1f;
        Plane slicePlane;
        Vector3 planeNormal;
        float planeOffset;

        FarseerPhysics.Dynamics.Category staticGeomCollisionCat = FarseerPhysics.Dynamics.Category.All;
        FarseerPhysics.Dynamics.Category staticGeomHitsCollisionCat = FarseerPhysics.Dynamics.Category.All;

        public StaticGeomFromMeshCmp()
            : base()
        {
        }


        public override void Set(Parameters parms)
        {          
            modelName = parms.GetValueAs<String>("ModelName", "none");
            Model model = Actor.GameServices().AssetGameService.Load<Model>(modelName);

            if (model == null)
                Logger.Error(this, "Missing modelAssetName / model");

            planeNormal = parms.GetValueAs<Vector3>("PlaneNormalXYZ", new Vector3(0, 0, 1));
            planeOffset = parms.GetValueAs<float>("PlaneOffsetDist", 0f);
            slicePlane = new Plane(planeNormal, planeOffset);

            shortEdgeTol = parms.GetValueAs<float>("ShortEdgeTolerance", 0.1f);

            int collisionCat = parms.GetValueAs<int>("CollisionCategory", (int)FarseerPhysics.Dynamics.Category.All);
            staticGeomCollisionCat = (FarseerPhysics.Dynamics.Category)collisionCat;

            int hitsCollisionCat = parms.GetValueAs<int>("HitsCollisionCategory", (int)FarseerPhysics.Dynamics.Category.All);
            staticGeomHitsCollisionCat = (FarseerPhysics.Dynamics.Category)hitsCollisionCat;
        }

        public override List<ParameterDescription> GetParameterDescriptions()
        {
            List<ParameterDescription> p = new List<ParameterDescription>();
            p.Add(new ParameterDescription("ModelName", "Model asset name", typeof(String), "none"));
            p.Add(new ParameterDescription("PlaneNormalXYZ", "plane normal XYZ for slicing mesh", typeof(Vector3), new Vector3(0,0,1)));
            p.Add(new ParameterDescription("PlaneOffsetDist", "Offset along normal from origin", typeof(float), 0f));
            p.Add(new ParameterDescription("ShortEdgeTolerance", "Minimum length of edge to keep", typeof(float), 0.1f));
            p.Add(new ParameterDescription("CollisionCategory", "Catagory for farseer collisions. Defaults to 'All'", typeof(int), (int)FarseerPhysics.Dynamics.Category.All));
            p.Add(new ParameterDescription("HitsCollisionCategory", "Catagory for farseer collisions. Defaults to 'All'", typeof(int), (int)FarseerPhysics.Dynamics.Category.All));
            return p;
        }

        override public Boolean InitComponent()
        {
            //create our wall segments and exit out.
            //TODO: take into consideration out Position, Forward, and Scale
            Model m = Actor.GameServices().AssetGameService.Load<Model>(modelName);

            List<LineSegment> sl = Slicer.Slice(m, slicePlane);

            sl = Slicer.RemoveShortEdges(sl, shortEdgeTol);

            //for(int a=0; a< 3; ++a)
            //  sl = Slicer.MergeSegments(sl, 0.80f, 0.1f);
            //sl = Slicer.MergeSegments(sl, 0.50f, 0.005f, 0.005f);
            int num = sl.Count;
            int diff = 1;
            int loops = 0;
            //try to reduce amount of static geometry. i think there are a lot of coincident lines.
            while (diff != 0 && num > 1000 && loops < 100)
            {
                sl = Slicer.MergeSegments(sl, 0.75f, 0.1f);
                diff = num - sl.Count;
                num = sl.Count;
                ++loops;
            }

            foreach (LineSegment seg in sl)
                createWallSegment(Actor.Scene, seg);

            return true;
        }


        private void createWallSegment(IScene scene, LineSegment seg)
        {
            IActor actor = scene.CreateEmptyActor();
            //the position will be set to the correct position by the physics body
            actor.AttachComponent(new PositionCmp());
            FarseerPhysicsCmp pb = new EdgeBodyCmp(scene.Name, seg.points[0], seg.points[1]);
            pb.Enabled = true;
            pb.IsStatic = true;
            actor.AttachComponent(pb);

            actor.Initialize();

            pb.Body.CollisionCategories = staticGeomCollisionCat;
            pb.Body.CollidesWith = staticGeomHitsCollisionCat;

        }
    }
}
